tree grammar WJTreeWalker; // the generated file is WJTreeWalker.java  
  
options { 
  tokenVocab=WJ; // this means to use the TL.tokens file 
  ASTLabelType=CommonTree; 
}  
  
@header { 
  package wj;
  import wj.tree.*;
  import wj.tree.functions.*;
  import java.util.HashMap;
} 

@members{
	
	private Scope currentScope = Scope.GLOBAL_SCOPE;
	
	public WJTreeWalker(CommonTreeNodeStream nodes, Scope currentScope){
		super(nodes);
		if(currentScope != null)
			this.currentScope = currentScope;
	}
}


walk returns [WJNode node]
	:	block {node = $block.node;}
	;  
  
block  returns [WJNode node]
@init{
	BlockNode blockNode = new BlockNode();
	node = blockNode;
}
	:	^(BLOCK ^(STATEMENTS (statement{blockNode.addStatement($statement.node);})*) ^(RETURN (expression{blockNode.setReturn($expression.node);})?))
	;  
  
statement  returns [WJNode node]
	:	assignment {node = $assignment.node;}
	|	functionCall {node = $functionCall.node;}
	|	ifStatement {node = $ifStatement.node;}
	|	whileStatement {node = $whileStatement.node;}
	;  
		
assignment returns [WJNode node]
	: 	^(ASSIGNMENT Identifier expression) { node = new AssignmentNode(currentScope, $Identifier.text, $expression.node);}
	;

argumentList returns [ArrayList<WJNode> argList]
@init{
	argList = new ArrayList<WJNode>();
}
	:	^(ARG_LIST (expression{argList.add($expression.node);})+)
	;

parameterList
  :  ^(PARAM_LIST Identifier+)
  ;
	
functionCall returns [WJNode node]
@init{
	ConnectNode connectNode = new ConnectNode();
}
	:	^(FUNCTION_CALL Write expression?) { node = new WriteNode($expression.node); }
	|	^(FUNCTION_CALL WriteLine expression?) {node = new WriteLineNode($expression.node);}
	|	^(FUNCTION_CALL ReadString) {node = new ReadStringNode();}
	|	^(FUNCTION_CALL ReadNumber) {node = new ReadNumberNode();}
	|	^(FUNCTION_CALL Connect(expression{connectNode.addNodeToConnect($expression.node);})+) {node = connectNode;}
	|	^(FUNCTION_CALL CharAt stringExpr expression) { node = new CharAtStringNode($stringExpr.node, $expression.node);}
	|	^(FUNCTION_CALL Length stringExpr) {node = new StringLengthNode($stringExpr.node);}
	|	^(FUNCTION_CALL Take expression stringExpr) { node = new TakeNode($expression.node, $stringExpr.node);}
	|	^(FUNCTION_CALL Drop expression stringExpr) {node = new DropNode($expression.node, $stringExpr.node);}
	|	^(FUNCTION_CALL Identifier argumentList?) {  node = new FunctionCallNode($Identifier.text, $argumentList.argList);}
	;
	
stringExpr returns [WJNode node]
	:	String	{node = new AtomNode(new WJValue($String.text));}
	|	Identifier {node = new RetrieveNode(currentScope, $Identifier.text);}
	;
		
ifStatement returns [WJNode node]
@init{
	IfStatementNode ifNode = new IfStatementNode();
	node = ifNode;
}
	:	^(IF ifStatementPart{ifNode.addIfBlock($ifStatementPart.blockNode);} (elseIfStatementPart{ifNode.addIfBlock($elseIfStatementPart.blockNode);})* (elseStatementPart{ifNode.addIfBlock($elseStatementPart.blockNode);})?)
	;
	
ifStatementPart returns [IfBlockNode blockNode]
	:	^(IF expression block){blockNode = new IfBlockNode($expression.node, $block.node);}
	;
	
elseIfStatementPart returns [IfBlockNode blockNode]
	:	^(ELSE_IF expression block) {blockNode = new IfBlockNode($expression.node, $block.node);}
	;
	
elseStatementPart returns [IfBlockNode blockNode]
	:	^(ELSE block) {blockNode = new IfBlockNode(new AtomNode(new WJValue(true)), $block.node);}
	;
		
whileStatement returns [WJNode node]
	:	^('while' expression 'is' 'correct' ':' block 'end while'){node = new WhileNode($expression.node, $block.node);}
	;
	
expression returns [WJNode node]
@init{
	ExprNode en = new ExprNode();
	node = en;
}
	:	^(OR (andExpr{en.add($andExpr.node);})+) {en.setMode(ExprNode.OR);}
	;
	
andExpr returns [WJNode node]
@init{
	ExprNode expr = new ExprNode();
	node = expr;
}
	:	^(AND (relExpr{expr.add($relExpr.node);})+) { expr.setMode(ExprNode.AND);}
	;
	
relExpr returns [WJNode node]
@init{
	ExprNode expr = new ExprNode();
	node = expr;
	expr.setMode(ExprNode.NOTHING);
}
	:	^(RELATIONAL (relOp{expr.setMode($relOp.op);})? (arimExpr{expr.add($arimExpr.node);})+)	
	;

relOp returns [int op]
	:	'=='	{op = ExprNode.EQUAL;}
	|	'!='	{op = ExprNode.NOT_EQUAL;}
	|	'>='	{op = ExprNode.GREATER_EQUAL;}
	|	'<='	{op = ExprNode.LESSER_EQUAL;}
	|	'>'		{op = ExprNode.GREATER;}
	|	'<'		{op = ExprNode.LESSER;}
	;
	
arimExpr returns [WJNode node]
@init{
	ExprNode expr = new ExprNode();
	node = expr;
	expr.setMode(ExprNode.NOTHING);
}
	:	^(ADDSUB (addSub{{expr.setMode($addSub.op);expr.addOp($addSub.op);}})* (mulExpr{expr.add($mulExpr.node);})+)		
	;

addSub returns [int op]
	:	'+'		{op = ExprNode.ADD;}
	|	'-'		{op = ExprNode.SUB;}
	;

mulExpr returns [WJNode node]
@init{
	ExprNode expr = new ExprNode();
	node = expr;
	expr.setMode(ExprNode.NOTHING);
}
	:	^(MULDIVMOD (mulDivMod{expr.setMode($mulDivMod.op); expr.addOp($mulDivMod.op);})* (powExpr{expr.add($powExpr.node);})+)	
	;
	
mulDivMod returns [int op]
	:	'*'		{op = ExprNode.MULT;}
	|	'/'		{op = ExprNode.DIV;}
	|	'mod'	{op = ExprNode.MOD;}
	;

powExpr returns [WJNode node]
@init{
	ExprNode expr = new ExprNode();
	node = expr;
	expr.setMode(ExprNode.NOTHING);
}
	:	^(POW (unaryExpr{expr.add($unaryExpr.node);})+)	{expr.setMode(ExprNode.POW);}
	;
  
unaryExpr returns [WJNode node]
	:	^('-' atom)			{node = new ExprNode($atom.node, ExprNode.NEG);}
	|	^('!' atom)				{node = new ExprNode($atom.node, ExprNode.NOT);}
	|	atom	{node = $atom.node;}
	;
	
atom returns [WJNode node]
	:	String 		{node = new AtomNode(new WJValue($String.text));}
	|	Number 		{node = new AtomNode(new WJValue($Number.text));}
	|	Boolean		{node = new AtomNode(new WJValue($Boolean.text));}	
	|	Identifier 	{node = new RetrieveNode(currentScope, $Identifier.text);}
	|	functionCall {node = $functionCall.node;}
	|	'(' expression ')' {node = $expression.node;}
	;